//本算法的精髓在于每个合数都只会被其最小质因数筛去
#include <iostream>
#include <cstring>
using namespace std;

const int N = 1000010;

bool st[N];   //记录是否被筛了
int prime[N]; //记录素数
int cnt = 0;  //记录素数的数目
int n;        //获取输入

int main()
{
    memset(st, false, sizeof st);
    cin >> n;
    for (int i = 2; i <= n; ++i) //因为是从1~n的全部质数，所以并不排除连n都是质数的可能，所以这里必须从2到n
    {
        if (!st[i])
            prime[cnt++] = i;                   //如果遍历到这个数，这个数还没被筛去，证明这个数是质数
        for (int j = 0; prime[j] <= n / i; ++j) //这里用n/i作为条件是为了防止爆数组，如果用全部素数访问，当n=15、i=5时，会一直筛到25
        {
            st[prime[j] * i] = true; //因为是顺序遍历，所以不论怎么样，pj一定是pj*i的最小质因数
            //当i%pj==0时，那么pj一定是i的最小质因数，因为如果前面有更小的质因数，已经通过下面的语句退出循环了，所以pj也一定是pj*i的最小质因数
            //当i%pj!=0时，既pj不是i的最小质因数,那么pj*i的最小质因数一定是pj
            //以上既为唯一性证明
            //完全性证明，因为一个合数一定会被分成最小质因数和一个非1正整数的数和一个最小质因数的乘积，所以一定会被筛
            if (i % prime[j] == 0) //因为之后的就不能保证pj*i是最小质因数了，所以退出
                break;
        }
    }
    cout << cnt;
    return 0;
}